#pragma once 

#include <queue>
#include <pthread.h>
#include <iostream>
using namespace std;

// 线程池线程个数缺省值
const size_t NUM = 5;

template<class T>
class ThreadPool 
{
  public:
    // 构造函数，负责初始化成员变量和创建线程
    ThreadPool(const size_t num = NUM)
      :_threadNum(num)
      ,_tids(num)
    {
      pthread_mutex_init(&_lock, nullptr);
      pthread_cond_init(&_empty, nullptr);

      pthread_t id;
      for(size_t i = 0; i < _threadNum; ++i)
      {
        pthread_create(&id, nullptr, Routine, this);
        _tids[i] = id;
      }
    }

    // 析构函数中负责销毁互斥锁、条件变量和线程池中的线程
    ~ThreadPool()
    {
      pthread_mutex_destroy(&_lock);
      pthread_cond_destroy(&_empty);

      for(size_t i = 0; i < _threadNum; ++i)
      {
        pthread_cancel(_tids[i]);
      }
    }

    // 任务队列判空
    bool Empty()
    {
      return _taskQueue.empty();
    }

    // 申请任务队列的互斥锁
    void Lock()
    {
      pthread_mutex_lock(&_lock);
    }

    // 释放任务队列的互斥锁
    void UnLock()
    {
      pthread_mutex_unlock(&_lock);
    }

    // 任务队列为空时，线程在_empty条件变量下等待
    void Wait()
    {
      pthread_cond_wait(&_empty, &_lock);
    }

    // 唤醒一个在_empty下等待的线程
    void WakeUp()
    {
      pthread_cond_signal(&_empty);
    }

    // 外部调用该函数向任务队列中插入一个任务
    void Push(const T& task)
    {
      Lock();
      _taskQueue.push(task);
      UnLock();
      WakeUp();
    }

  private:
    // 线程池中的线程调用该成员函数获得任务队列中的一个任务
    void Pop(T& task)
    {
      task = _taskQueue.front();
      _taskQueue.pop();
    }

    // 线程池中的线程执行该函数不断从任务队列中拿任务并解决任务
    static void* Routine(void* arg)
    {
      pthread_detach(pthread_self());
      ThreadPool* argThis = (ThreadPool*)arg;
      while(true)
      {
        argThis->Lock();
        while(argThis->Empty())
        {
          argThis->Wait();
        }
        T task;
        argThis->Pop(task);
        argThis->UnLock();

        task.Run();
      }
    }

    // 成员变量
    vector<pthread_t> _tids;// 记录池中所有线程的线程id
    size_t _threadNum;      // 记录线程池中线程的数量
    queue<T> _taskQueue;    // 任务队列，用于存储push进来的任务
    pthread_mutex_t _lock;  // 保证任务队列数据安全的互斥锁
    pthread_cond_t _empty;  // 队列为空时线程在该条件变量下等待
};
